# -*- coding:utf-8 -*-

# @File     :general
# @Author   :Yupeng
# @Date     :2018/10/26 10:24
""""

"""
import json
import random
import string

from datetime import datetime
from functools import wraps
from bson.objectid import ObjectId
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone
from django.http import HttpResponse
from django.utils.encoding import force_text
from django.utils.functional import Promise
from django.utils.translation import ugettext as _

from hte.error.handle import abort_on_error
from hte.error.models import HTEError
from .middleware import GlobalRequestMiddleware
from django.conf import settings
from itsdangerous import TimedJSONWebSignatureSerializer, BadSignature, SignatureExpired, BadTimeSignature


def _get_type_name(class_type):
    '''
    方法前加_,表示该方法默认只在该模块或者该类中使用，import*默认不会导入该方法
    :param class_type:
    :return:
    '''
    if class_type == str:
        return 'string'
    if class_type == int:
        return 'integer'
    if class_type == float:
        return 'float'
    if class_type == bool:
        return 'boolean'
    if class_type == object:
        return 'object'
    if class_type == list:
        return 'array'

    return str(class_type)


class LazyEncoder(DjangoJSONEncoder):
    def default(self, obj):
        if isinstance(obj, (Promise, ObjectId)):
            return force_text(obj)
        if isinstance(obj, datetime):
            obj = timezone.localtime(obj)
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        return super(LazyEncoder, self).default(obj)


def generate_token(data, expires_in=3600):
    s = TimedJSONWebSignatureSerializer(secret_key=settings.SECRET_KEY, expires_in=expires_in)
    return s.dumps(data).decode()


def decode_token(token):
    s = TimedJSONWebSignatureSerializer(secret_key=settings.SECRET_KEY)
    try:
        return s.loads(token)
    except (BadTimeSignature, SignatureExpired, BadSignature):
        return None


def json_response(data=None, msg: str = '', status_code: int = 200, **kwargs) -> HttpResponse:
    """"
    将字典数据转为JSON返回（正常返回数据，错误代码为0即成功）
    :param data: 需要返回的数据
    :param msg: 提示消息
    :param status_code: http 状态码，默认 200
    :param kwargs: 附加信息，直接嵌入JSON第一级
    :return: None
    """
    result = {'code': HTEError.SUCCESS,
              'data': data}
    if msg:
        result['msg'] = msg
    if kwargs:
        result['extra'] = kwargs
    return HttpResponse(json.dumps(result, ensure_ascii=False, cls=LazyEncoder),
                        content_type='application/json; charset=utf-8',
                        status=status_code)


def load_request_body(request=None, expected=object):
    """"
    获取用户请求中的原始JSON数据
    :return: JSON字典
    """
    try:
        if not request:
            request = GlobalRequestMiddleware.get_current_request()
        json_data = json.loads(request.body)
        if not isinstance(json_data, expected):
            abort_on_error(HTEError.NOT_ACCEPTABLE, 'expect %s but %s given' % (type(expected), type(json_data)))
        return json_data
    except Exception as ex:
        abort_on_error(HTEError.NOT_ACCEPTABLE, str(ex))


def get_json_field(field, required_type=object, allow_none=False, allowed=None, force=False, default=None):
    """
    获取JSON中的字段值，若发生错误则终止响应
    :param field: JSON字段名
    :param required_type: 要求的数据类型
    :param allow_none: 是否可以为null或者为空白
    :param allowed: 若此项不为None则参数的值只能选取allowed（list）中的值
    :param force: 如果为True，则发生任何错误都返回None而不是中断响应
    :return: 字段的值
    """

    try:
        json_data = json.loads(GlobalRequestMiddleware.get_current_request().body)
    except:
        if not force:
            abort_on_error(HTEError.NOT_ACCEPTABLE)
        return default

    value = json_data.get(field)
    if value is None and not allow_none:
        if not force:
            abort_on_error(HTEError.FIELD_MISSING, error_detail=_('field "%(field_name)s" is missing') % {'field_name': field})
    elif value is None and allow_none:
        return default
    elif isinstance(value, required_type):
        if allowed is not None and value not in allowed:
            if not force:
                abort_on_error(HTEError.WRONG_FIELD_TYPE, error_detail=_('Value of field "%(field)s" should be one of [%(allowed_values)s].') % {
                    'field': field, 'allowed_values': ','.join(allowed)})
        return value
    else:
        abort_on_error(HTEError.WRONG_FIELD_TYPE, error_detail=_('Field "%(field)s" should be %(required_type)s.') % {
            'field': field, 'required_type': _get_type_name(required_type)})


def get_json_field_r(request, field, required_type=object, allow_none=False, allowed=None, force=False, default=None):
    """
    从http请求中获取JSON中的字段值，若发生错误则终止响应
    :param field: JSON字段名
    :param required_type: 要求的数据类型
    :param allow_none: 是否可以为null或者为空白
    :param allowed: 若此项不为None则参数的值只能选取allowed（list）中的值
    :param force: 如果为True，则发生任何错误都返回None而不是中断响应
    :return: 字段的值
    """

    try:
        json_data = json.loads(request.body)
    except json.JSONDecodeError:
        if not force:
            abort_on_error(HTEError.NOT_ACCEPTABLE)
        return default

    value = json_data.get(field)
    if value is None and not allow_none:
        if not force:
            abort_on_error(HTEError.FIELD_MISSING,
                           error_details=_('field "%(field_name)s" is missing') % {'field_name': field})
    elif value is None and allow_none:
        return default
    elif isinstance(value, required_type):
        if allowed is not None and value not in allowed:
            if not force:
                abort_on_error(HTEError.WRONG_FIELD_TYPE,
                               error_details=_('Value of field "%(field)s" should be one of [%(allowed_values)s].') % {
                                   'field': field, 'allowed_values': ','.join(allowed)})
        return value
    else:
        abort_on_error(HTEError.WRONG_FIELD_TYPE, error_details=_('Field "%(field)s" should be %(required_type)s.') % {
            'field': field, 'required_type': _get_type_name(required_type)})


def get_param(field, allow_none=True, allowed=None, convert_to=None, force=False):
    """"
    获取HTTP的GET参数，若发生错误则终止响应
    :param field: 字段名
    :param allow_none: 是否可以为空
    :param allowed: 若此项不为None则参数的值只能选取allowed（list）中的值
    :param force: 如果为True，则发生任何错误都返回None而不是中断响应
    :return: 字段值
    """
    value = GlobalRequestMiddleware.get_current_request().GET.get(field, None)
    if value is None and not allow_none:
        if not force:
            abort_on_error(HTEError.FIELD_MISSING)
        return None
    elif value is None and allow_none:
        return None
    elif allowed is not None and value not in allowed:
        if not force:
            abort_on_error(HTEError.WRONG_FIELD_TYPE)
            return None
    if convert_to:
        try:
            return None if value is None else convert_to(value)
        except:
            if not force:
                abort_on_error(HTEError.WRONG_FIELD_TYPE)
            else:
                return None
    else:
        return None if value is None else value.strip()


def get_remote_ip():
    """
    返回发起请求的主机IP地址
    :return: IP地址
    """
    request = GlobalRequestMiddleware.get_current_request()
    if 'HTTP_X_FORWARDED_FOR' in request.META:
        return request.META['HTTP_X_FORWARDED_FOR']
    else:
        return request.META['REMOTE_ADDR']


def require_methods_api(request_methods_list):
    '''

    :param request_methods_list:
    :return:
    '''

    def decorator(func):
        @wraps(func)
        def inner(request, *args, **kwargs):
            if request.method not in request_methods_list:
                abort_on_error(HTEError.METHON_NOT_ALLOWED)
            return func(request, *args, **kwargs)

        return inner

    return decorator


require_GET_api = require_methods_api(['GET'])
require_POST_api = require_methods_api(['POST'])
require_PATCH_api = require_methods_api(['PATCH'])
require_PUI_api = require_methods_api(['PUT'])


def get_current_username(default='_system') -> str:
    try:
        req = GlobalRequestMiddleware.get_current_request()
        cur_user = getattr(req, 'user')
        if cur_user:
            return cur_user.username
    except:
        pass
    return default


def patch_resource(obj, fields: (tuple, list), data: dict, action_map: dict = dict):
    """
    更新资源
    :param obj: 待更新对象
    :param fields: 待检测字段
    :param data: 数据集 QueryDict
    :param action_map: 设置值的操作，默认为直接赋值，即： lambda o, f, v: setattr(o, f, v)
            如若设置，则应该为字段名对应一个设置函数，函数包含 o, f, d 三个参数，分别代表对象、字段名和值
    :return:
    """

    def action_default(o, f, v):
        setattr(o, f, v)

    changed_fields = list()
    # action_default = lambda o, f, d: setattr(o, f, d)
    for field in fields:
        if field in data:
            action = action_map.get(field, action_default)
            action(obj, field, data.get(field))
            changed_fields.append(field)
    return obj, changed_fields


def gen_secret_key(length=48):
    characters = string.ascii_letters + string.digits
    return ''.join([random.choice(characters) for _ in range(length)])
